<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Title</title>
</head>
<body>

<script type="text/javascript" src="libs/zepto.js" ></script>
<script type="text/javascript">

//这个为js罗杰部分实现,后续会释放工厂方法
class PageClass {
  //构造函数,传入对象
  constructor(opts) {

    //必须拥有的参数
    this.data = {};
    Object.assign(this, opts);
  }

  //核心方法,每个Page对象需要一个模板实例
  setView(view) {
    this.view = view;
  }

  //核心方法,设置数据后会引发页面刷新
  setData(data) {
    Object.assign(this.data, data);

    //只影响改变的数据
    this.view.reRender(data, this.data)
  }

  render() {
    this.view.setInitData(this.data);
    this.view.render();

    if(this.onLoad) this.onLoad();
  }

}

//View为模块的实现,主要用于解析目标生产node
class View {
  constructor(template) {
    this.template = template;

    //由控制器page传入的初始数据或者setData产生的数据
    this.data = {};

    this.labelMap = {
      'view': 'div',
      '#text': 'span'
    };

    this.nodes = {};
    this.root = {};
  }

  setInitData(data) {
    this.data = data;
  }

  //数据便会引起的重新渲染
  reRender(data, allData) {
    this.data = allData;
    let k, v, i, len, j, len2, v2;

    //开始重新渲染逻辑,寻找所有保存了的node
    for(k in data) {
      if(!this.nodes[k]) continue;
      for(i = 0, len = this.nodes[k].length; i < len; i++) {
        for(j = 0; j < this.nodes[k][i].length; j++) {
          v = this.nodes[k][i][j];
          if(v.type === 'text') {
            v.node.innerText = data[k];
          } else if(v.type === 'attr') {
            v.node.setAttribute(v.name, data[k]);
          }
        }
      }
    }
  }
  /*
    传入一个节点,解析出一个节点,并且将节点中的数据以初始化数据改变
    并且将其中包含{{}}标志的节点信息记录下来
  */
  _handlerNode (node) {

    let reg = /\{\{([\s\S]+?)\}\}/;
    let result, name, value, n, map = {};
    let attrs , i, len, attr;

    name = node.nodeName;
    attrs = node.attributes;
    value = node.nodeValue;
    n = document.createElement(this.labelMap[name.toLowerCase()] || name);

    //说明是文本,需要记录下来了
    if(node.nodeType === 3) {
      n.innerText =  this.data[value] || '';

      result =  reg.exec(value);
      if(result) {
        n.innerText =  this.data[result[1]] || '';

        if(!map[result[1]]) map[result[1]] = [];
        map[result[1]].push({
          type: 'text',
          node: n
        });
      }
    }

    if(attrs) {
      //这里暂时只处理属性和值两种情况,多了就复杂10倍了
      for (i = 0, len = attrs.length; i < len; i++) {
        attr = attrs[i];
        result = reg.exec(attr.value);

        n.setAttribute(attr.name, attr.value);
        //如果有node需要处理则需要存下来标志
        if (result) {
          n.setAttribute(attr.name, this.data[result[1]] || '');

          //存储所有会用到的节点,以便后面动态更新
          if (!map[result[1]]) map[result[1]] = [];
          map[result[1]].push({
            type: 'attr',
            name: attr.name,
            node: n
          });

        }
      }
    }

    return {
      node: n,
      map: map
    }

  }

  //遍历一个节点的所有子节点,如果有子节点继续遍历到没有为止
  _runAllNode(node, map, root) {

    let nodeInfo = this._handlerNode(node);
    let _map = nodeInfo.map;
    let n = nodeInfo.node;
    let k, i, len, children = node.childNodes;

    //先将该根节点插入到上一个节点中
    root.appendChild(n);

    //处理map数据,这里的map是根对象,最初的map
    for(k in _map) {
      if(!map[k]) map[k] = [];
      map[k].push(_map[k]);
    }

    for(i = 0, len = children.length; i < len; i++) {
      this._runAllNode(children[i], map, n);
    }

  }

  //处理每个节点,翻译为页面识别的节点,并且将需要操作的节点记录
  splitTemplate () {
    let nodes = $(this.template);
    let map = {}, root = document.createElement('div');
    let i, len;

    for(i = 0, len = nodes.length; i < len; i++) {
      this._runAllNode(nodes[i], map, root);
    }

    this.nodes = map;
    this.root = root;
  }

  render() {
    let i, len;
    this.splitTemplate();
    for(i = 0, len = this.root.childNodes.length; i< len; i++)
      document.body.appendChild(this.root.childNodes[0]);
  }

}

function Page (data) {
  let page = new PageClass(data);
  return page;
}

function main() {
  let view = new View('<view>{{pageShow}}</view><view class="ddd" is-show="{{pageShow}}" >{{pageShow}}<view class="c1">{{pageData}}</view></view>');
  let page = Page({
    data: {
      pageShow: 'pageshow',
      pageData: 'pageData',
      pageShow1: 'pageShow1'
    },
    onLoad: function () {
      this.setData({
        pageShow: '我是pageShow啊'
      });
    }
  });

  page.setView(view);
  page.render();
}

main();

</script>
</body>
</html>